package com.kamistoat.meimeichat.meimeichatclient.socket.handlers;

import com.kamistoat.meimeichat.meimeichatclient.application.UIServiceConnector;
import com.kamistoat.meimeichat.meimeichatclient.config.NettyClientConstant;
import com.kamistoat.meimeichat.meimeichatclient.infrastructure.util.BeanUtil;
import com.kamistoat.meimeichat.meimeichatclient.infrastructure.util.CacheUtil;
import com.kamistoat.meimeichat.meimeichatclient.kcp.P2PDatagramHandler;
import com.kamistoat.meimeichat.meimeichatclient.socket.NettyClientBizHandler;
import com.kamistoat.meimeichatserveraggrement.protocol.Command;
import com.kamistoat.meimeichatserveraggrement.protocol.p2p.TCP_EstablishP2PServer2Client;
import com.kamistoat.meimeichatserveraggrement.protocol.p2p.UDP_AnswerP2PClient2Client;
import com.kamistoat.meimeichatserveraggrement.protocol.p2p.UDP_AnswerP2PClient2Server;
import com.kamistoat.meimeichatserveraggrement.util.SerializationUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramPacket;
import sun.misc.Cache;

import java.net.InetSocketAddress;
import java.util.Date;

/**
 * A试图建立与B的UDP连接，B收到服务器通知，确保B->A建立成功
 */
public class TCP_EstablishP2PServer2ClientHandler extends NettyClientBizHandler<TCP_EstablishP2PServer2Client> {

    public TCP_EstablishP2PServer2ClientHandler(UIServiceConnector uiServiceConnector) {
        super(uiServiceConnector);
    }

    /**
     * Warning:子类只需要重写该方法
     *
     * @param channel
     * @param msg
     */
    @Override
    public void channelRead(Channel channel, TCP_EstablishP2PServer2Client msg) {
        InetSocketAddress targetAddress =
                new InetSocketAddress(msg.getTargetUserHost(), msg.getTargetUserPort());

        logger.info("收到服务器通知，建立与用户 {} 的NET映射，对方UDP地址为 {}", msg.getTargetUserId(), targetAddress);

        // 两种停止P2P建立的边界条件
        if (CacheUtil.p2pDateMap.containsKey(msg.getTargetUserId()) &&
                new Date().getTime() - CacheUtil.p2pDateMap.get(msg.getTargetUserId()).getTime() <= NettyClientConstant.P2P_TIMEOUT) {
            logger.info("与用户 {} 的NET映射已存在，无需重新建立...", msg.getTargetUserId());
            return;
        }else if(CacheUtil.p2pFailureDateMap.containsKey(msg.getTargetUserId()) &&
                new Date().getTime() - CacheUtil.p2pFailureDateMap.get(msg.getTargetUserId()).getTime() <= NettyClientConstant.P2P_TIMEOUT) {
            logger.info("当前与用户 {} 的NET映射无法建立，对方路由器可能不支持P2P链接，请稍后再尝试...", msg.getTargetUserId());
            return;
        }

        // 更新对方的UDP地址
        CacheUtil.p2pMap.put(msg.getTargetUserId(), targetAddress);

        Channel clientP2PChannel = BeanUtil.getBean("ClientP2PChannel", Channel.class);

        // 向对方的UDP地址发送一条半确认状态的 UDP_AnswerP2PClient2Client。如果此消息被对方接收，则说明整个打洞已经完成。否则还需要继续对方->自身的打洞流程
        UDP_AnswerP2PClient2Client udp_answerP2PClient2Client = new UDP_AnswerP2PClient2Client(
                CacheUtil.userId, NettyClientConstant.UDP_ANSWER_CONFIRM_STATUS.Half_Confirm.getCode());
        P2PDatagramHandler.sendUDPAnswerC2C(targetAddress, clientP2PChannel, udp_answerP2PClient2Client);

        // 向服务器发送应答信息。告知P2P连接的一端已完成
        UDP_AnswerP2PClient2Server udp_answerP2PClient2Server = new UDP_AnswerP2PClient2Server(CacheUtil.userId, msg.getTargetUserId());
        byte[] client2ServerMsgBytes = SerializationUtil.serialize(udp_answerP2PClient2Server);
        ByteBuf client2ServerMsgBuf = Unpooled.buffer();
        client2ServerMsgBuf.writeInt(client2ServerMsgBytes.length + 1);
        client2ServerMsgBuf.writeByte(Command.UDP_AnswerP2PClient2Server);
        client2ServerMsgBuf.writeBytes(client2ServerMsgBytes);
        clientP2PChannel.writeAndFlush(
                new DatagramPacket(client2ServerMsgBuf,
                        new InetSocketAddress(NettyClientConstant.NETTY_SERVER_P2P_HOST, NettyClientConstant.NETTY_SERVER_P2P_PORT)));
        logger.info("已建立自身到用户 {} 的NET映射，等待对方建立...", msg.getTargetUserId());
    }
}
